import java.io.*;
import java.util.*;
import java.text.*;

public class PrisonersDilemmaProblem extends FitnessFunction{

/*******************************************************************************
*                            INSTANCE VARIABLES                                *
*******************************************************************************/


/*******************************************************************************
*                            STATIC VARIABLES                                  *
*******************************************************************************/
	static int maxSteps = 100;


/*******************************************************************************
*                              CONSTRUCTORS                                    *
*******************************************************************************/

	public PrisonersDilemmaProblem(){
		name = "Prisoner Dilemma Problem";
	}

/*******************************************************************************
*                                MEMBER METHODS                                *
*******************************************************************************/

//  COMPUTE A CHROMOSOME'S RAW FITNESS *************************************

	public void doRawFitness(Chromo X){

		X.rawFitness = 0;
	
		Strategy player1, player2;
		IteratedPD ipd;
	
		int numberOfOpponents = 6;
		
		// play against 4 deterministic prisoner dilemma strategies
		player1 = new StrategyAlwaysDefect();
		player2 = new StrategyGene(X.chromo);
		ipd = new IteratedPD(player1, player2);
		ipd.runSteps(maxSteps);
		X.rawFitness = ipd.player2Score();
	
		//System.out.println(" Player 1 score = " +  ipd.player1Score() + "\n");
		//System.out.println(" Player 2 score = " +  ipd.player2Score() + "\n");
		
		player1 = new StrategyAlwaysCooperate();
		player2 = new StrategyGene(X.chromo);
		ipd = new IteratedPD(player1, player2);
		ipd.runSteps(maxSteps);
		X.rawFitness += ipd.player2Score();

		player1 = new StrategyTitForTat();
		player2 = new StrategyGene(X.chromo);
		ipd = new IteratedPD(player1, player2);
		ipd.runSteps(maxSteps);
		X.rawFitness += ipd.player2Score();

		player1 = new StrategyTitForTwoTats();
		player2 = new StrategyGene(X.chromo);
		ipd = new IteratedPD(player1, player2);
		ipd.runSteps(maxSteps);
		X.rawFitness += ipd.player2Score();

		player1 = new StrategyRandom();
		player2 = new StrategyGene(X.chromo);
		ipd = new IteratedPD(player1, player2);
		ipd.runSteps(maxSteps);
		X.rawFitness += ipd.player2Score();
		
		player1 = new StrategyPovlov();
		player2 = new StrategyGene(X.chromo);
		ipd = new IteratedPD(player1, player2);
		ipd.runSteps(maxSteps);
		X.rawFitness += ipd.player2Score();
		
		X.rawFitness = X.rawFitness/numberOfOpponents; 
	}

	public void doFitnessForPopulation(Chromo[] member){
		
		for (int i = 0; i < member.length; i++){
			member[i].rawFitness = 0;
		}
		int strat = Parameters.stratType;
			 
		switch(strat){
		case 1://Playing against all individuals
			doFitnessPopulationAgainstAll(member);			 
		break;
			 
		case 2: //Playing against K random players
			doFitnessAgainstRandomSample(member);
		break;
		case 3: //Playing against known strategies
			for (int i = 0; i < member.length; i++){
				doRawFitness(member[i]);
			}
		break;
		default:
			System.out.println("Invalid Strategy Type");
		}
	}
	
	public void doFitnessAgainstRandomSample(Chromo[] member){
		int size = Parameters.gameSize;
		Strategy player1, player2;
		IteratedPD ipd;
	
		for (int i = 0; i < 100; i++) {
		//select random K individuals
			int[] fightClub = new int[size];
			double[] newFitness = new double[size];
			
			// not checking for playing against self or same individual twice
			for (int j = 0; j < size; j++){
				newFitness[j] = 0;
				fightClub[j] = (int) (Search.r.nextDouble() * member.length);
			}
			// play against all other individuals
			for (int k = 0; k < size - 1; k++){
				for (int m = k+1; m < size; m++){
					player1 = new StrategyGene(member[fightClub[k]].chromo);
					player2 = new StrategyGene(member[fightClub[m]].chromo);
				  
					ipd = new IteratedPD(player1, player2);
				
					ipd.runSteps(maxSteps);
			  
					newFitness[k] += ipd.player1Score();
					newFitness[m] += ipd.player2Score();
				}
			}
			// check if fitness already existed for individuals and average for new fitness
			for (int k = 0; k < size - 1; k++){
				newFitness[k] = newFitness[k]/(size - 1);
				
				if (0 != member[fightClub[k]].rawFitness){
					member[fightClub[k]].rawFitness = (member[fightClub[k]].rawFitness + newFitness[k])/2;
				}
				else
					member[fightClub[k]].rawFitness = newFitness[k];
			}			
		}
	}
	
	public void doFitnessPopulationAgainstAll(Chromo[] member){
		
		Strategy player1, player2;
		IteratedPD ipd;
	
		// play against all other individuals
		for (int i = 0; i < member.length - 1; i++){
			for (int j = i+1; j < member.length; j++){
				player1 = new StrategyGene(member[i].chromo);
				player2 = new StrategyGene(member[j].chromo);
			  
				ipd = new IteratedPD(player1, player2);
			
				ipd.runSteps(maxSteps);
		  
				member[i].rawFitness += ipd.player1Score();
				member[j].rawFitness += ipd.player2Score();
			}
		}
		// Also play against tit for tat 
		for (int i = 0; i < member.length; i++){
			player1 = new StrategyGene(member[i].chromo);
			player2 = new StrategyTitForTat();
		  
			ipd = new IteratedPD(player1, player2);
		
			ipd.runSteps(maxSteps);
	  
			member[i].rawFitness += ipd.player1Score();
		}
		// Since add tit for tat need to divide by number of individuals(member.length) instead of 1 less 
		for (int i = 0; i < member.length; i++){
			member[i].rawFitness = member[i].rawFitness/(member.length);
		}
	}

//  PRINT OUT AN INDIVIDUAL GENE TO THE SUMMARY FILE *********************************

	public void doPrintGenes(Chromo X, FileWriter output) throws java.io.IOException{

		for (int i=0; i<Parameters.numGenes; i++){
			output.write("#" + X.chromo);
			//Hwrite.right(X.getGeneAlpha(i),11,output);
		}
		output.write("\n        ");
		output.write("#   RawFitness: ");
		for (int i=0; i<Parameters.numGenes; i++){
			Hwrite.right(X.getPosIntGeneValue(i),11,output);
		}
		Hwrite.right((int) X.rawFitness,13,output);
		output.write("\n\n");
		
		// Write as CDCDCD
		output.write("\r\n");
		for (int i=0; i<Parameters.geneSize; i++){
			String previousMoves = Integer.toBinaryString(i+64);
			String previousMovesCD = "";
			previousMoves = previousMoves.substring(1);
			
			for (int j = 0; j < previousMoves.length(); j++){
				if (previousMoves.charAt(j)== '1')
					previousMovesCD += 'C';
				else
					previousMovesCD += 'D';
			}
				
			output.write(previousMovesCD + ' ' + X.chromo.charAt(i) + "\r\n");
		}
		return;
	}

/*******************************************************************************
*                             STATIC METHODS                                   *
*******************************************************************************/

}   // End of PrisonerDilemma.java ******************************************************
